 const path = require('path')
const fs = require('fs')

const gulp = require('gulp')
const clean = require('gulp-clean')
const less = require('gulp-less')
const rename = require('gulp-rename')
const gulpif = require('gulp-if')
const sourcemaps = require('gulp-sourcemaps')
const webpack = require('webpack')
const gulpInstall = require('gulp-install')

const config = require('./config')
const checkComponents = require('./checkcomponents')
const checkWxss = require('./checkwxss')
const _ = require('./utils')

const jsConfig = config.js || {}
const wxssConfig = config.wxss || {}
const srcPath = config.srcPath
const distPath = config.distPath

/**
 * 获取 wxss 流
 */
function wxss(wxssFileList) {
  if (!wxssFileList.length) return false

  return gulp
    .src(wxssFileList, { cwd: srcPath, base: srcPath })
    .pipe(checkWxss.start()) // 开始处理 import
    .pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.init()))
    .pipe(gulpif(wxssConfig.less, less({ paths: [srcPath] })))
    .pipe(checkWxss.end()) // 结束处理 import
    .pipe(rename({ extname: '.wxss' }))
    .pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.write('./')))
    .pipe(_.logger(wxssConfig.less ? 'generate' : undefined))
    .pipe(gulp.dest(distPath))
}

/**
 * 获取 js 流
 */
function js(jsFileMap, scope) {
  const webpackConfig = config.webpack
  const webpackCallback = (err, stats) => {
    if (!err) {
      // eslint-disable-next-line no-console
      console.log(
        stats.toString({
          assets: true,
          cached: false,
          colors: true,
          children: false,
          errors: true,
          warnings: true,
          version: true,
          modules: false,
          publicPath: true,
        })
      )
    } else {
      // eslint-disable-next-line no-console
      console.log(err)
    }
  }

  webpackConfig.entry = jsFileMap
  webpackConfig.output.path = distPath

  if (scope.webpackWatcher) {
    scope.webpackWatcher.close()
    scope.webpackWatcher = null
  }

  if (config.isWatch) {
    scope.webpackWatcher = webpack(webpackConfig).watch(
      {
        ignored: /node_modules/,
      },
      webpackCallback
    )
  } else {
    webpack(webpackConfig).run(webpackCallback)
  }
}

/**
 * 拷贝文件
 */
function copy(copyFileList) {
  if (!copyFileList.length) return false

  return gulp
    .src(copyFileList, { cwd: srcPath, base: srcPath })
    .pipe(_.logger())
    .pipe(gulp.dest(distPath))
}

/**
 * 安装依赖包
 */
function install() {
  return gulp.series(
    async () => {
      const demoDist = config.demoDist
      const demoPackageJsonPath = path.join(demoDist, 'package.json')
      const packageJson = _.readJson(path.resolve(__dirname, '../package.json'))
      const dependencies = packageJson.dependencies || {}

      await _.writeFile(demoPackageJsonPath, JSON.stringify({ dependencies }, null, '\t')) // write dev demo's package.json
    },
    () => {
      const demoDist = config.demoDist
      const demoPackageJsonPath = path.join(demoDist, 'package.json')

      return gulp.src(demoPackageJsonPath).pipe(gulpInstall({ production: true }))
    }
  )
}

class BuildTask {
  constructor(id, entry) {
    if (!entry) return

    this.id = id
    this.entries = Array.isArray(config.entry) ? config.entry : [config.entry]
    this.copyList = Array.isArray(config.copy) ? config.copy : []
    this.componentListMap = {}
    this.cachedComponentListMap = {}

    this.init()
  }

  init() {
    const id = this.id

    /**
     * 清空目标目录
     */
    gulp.task(`${id}-clean-dist`, () =>
      gulp.src(distPath, { read: false, allowEmpty: true }).pipe(clean())
    )

    /**
     * 拷贝 demo 到目标目录
     */
    let isDemoExists = false
    gulp.task(
      `${id}-demo`,
      gulp.series(
        // async () => {
        //   const demoDist = config.demoDist

        //   isDemoExists = await _.checkFileExists(path.join(demoDist, 'project.config.json'))
        // },
        (done) => {
          if (!isDemoExists) {
            const demoSrc = config.demoSrc
            const demoDist = config.demoDist

            return gulp.src('**/*', { cwd: demoSrc, base: demoSrc }).pipe(gulp.dest(demoDist))
          }

          return done()
        }
      )
    )

    /**
     * 安装依赖包
     */
    gulp.task(`${id}-install`, install())

    /**
     * 检查自定义组件
     */
    gulp.task(`${id}-component-check`, async () => {
      const entries = this.entries
      const mergeComponentListMap = {}
      for (let i = 0, len = entries.length; i < len; i++) {
        let entry = entries[i]
        entry = path.join(srcPath, `${entry}.json`)
        const newComponentListMap = await checkComponents(entry)

        _.merge(mergeComponentListMap, newComponentListMap)
      }

      this.cachedComponentListMap = this.componentListMap
      this.componentListMap = mergeComponentListMap
    })

    /**
     * 写 json 文件到目标目录
     */
    gulp.task(`${id}-component-json`, (done) => {
      const jsonFileList = this.componentListMap.jsonFileList

      if (jsonFileList && jsonFileList.length) return copy(jsonFileList)

      return done()
    })

    /**
     * 拷贝 wxml 文件到目标目录
     */
    gulp.task(`${id}-component-wxml`, (done) => {
      const wxmlFileList = this.componentListMap.wxmlFileList

      if (
        wxmlFileList &&
        wxmlFileList.length &&
        !_.compareArray(this.cachedComponentListMap.wxmlFileList, wxmlFileList)
      ) {
        return copy(wxmlFileList)
      }

      return done()
    })

    /**
     * 生成 wxss 文件到目标目录
     */
    gulp.task(`${id}-component-wxss`, (done) => {
      const wxssFileList = ['index.wxss', ...this.componentListMap.wxssFileList]

      if (
        wxssFileList &&
        wxssFileList.length &&
        !_.compareArray(this.cachedComponentListMap.wxssFileList, wxssFileList)
      ) {
        return wxss(wxssFileList, srcPath, distPath)
      }

      return done()
    })

    /**
     * 生成 js 文件到目标目录
     */
    gulp.task(`${id}-component-js`, (done) => {
      const jsFileList = this.componentListMap.jsFileList

      if (
        jsFileList &&
        jsFileList.length &&
        !_.compareArray(this.cachedComponentListMap.jsFileList, jsFileList)
      ) {
        if (jsConfig.webpack) {
          js(this.componentListMap.jsFileMap, this)
        } else {
          return copy(jsFileList)
        }
      }

      return done()
    })

    /**
     * 拷贝相关资源到目标目录
     */
    gulp.task(
      `${id}-copy`,
      gulp.parallel(
        (done) => {
          const copyList = this.copyList
          const copyFileList = copyList
            .map((copyFilePath) => {
              try {
                if (fs.statSync(path.join(srcPath, copyFilePath)).isDirectory()) {
                  return path.join(copyFilePath, '**/*.!(wxss)')
                } else {
                  return copyFilePath
                }
              } catch (err) {
                // eslint-disable-next-line no-console
                console.error(err)
                return null
              }
            })
            .filter((copyFilePath) => !!copyFilePath)

          if (copyFileList.length) return copy(copyFileList)

          return done()
        },
        (done) => {
          const copyList = this.copyList
          const copyFileList = copyList
            .map((copyFilePath) => {
              try {
                if (fs.statSync(path.join(srcPath, copyFilePath)).isDirectory()) {
                  return path.join(copyFilePath, '**/*.wxss')
                } else if (copyFilePath.slice(-5) === '.wxss') {
                  return copyFilePath
                } else {
                  return null
                }
              } catch (err) {
                // eslint-disable-next-line no-console
                console.error(err)
                return null
              }
            })
            .filter((copyFilePath) => !!copyFilePath)

          if (copyFileList.length) return wxss(copyFileList, srcPath, distPath)

          return done()
        }
      )
    )

    /**
     * 监听 js 变化
     */
    gulp.task(`${id}-watch-js`, (done) => {
      if (!jsConfig.webpack) {
        return gulp.watch(
          this.componentListMap.jsFileList,
          { cwd: srcPath, base: srcPath },
          gulp.series(`${id}-component-js`)
        )
      }

      return done()
    })

    /**
     * 监听 json 变化
     */
    gulp.task(`${id}-watch-json`, () =>
      gulp.watch(
        this.componentListMap.jsonFileList,
        { cwd: srcPath, base: srcPath },
        gulp.series(
          `${id}-component-check`,
          gulp.parallel(
            `${id}-component-wxml`,
            `${id}-component-wxss`,
            `${id}-component-js`,
            `${id}-component-json`
          )
        )
      )
    )

    /**
     * 监听 wxml 变化
     */
    gulp.task(`${id}-watch-wxml`, () => {
      this.cachedComponentListMap.wxmlFileList = null
      return gulp.watch(
        this.componentListMap.wxmlFileList,
        { cwd: srcPath, base: srcPath },
        gulp.series(`${id}-component-wxml`)
      )
    })

    /**
     * 监听 wxss 变化
     */
    gulp.task(`${id}-watch-wxss`, () => {
      this.cachedComponentListMap.wxssFileList = null
      return gulp.watch(
        '**/*.wxss',
        { cwd: srcPath, base: srcPath },
        gulp.series(`${id}-component-wxss`)
      )
    })

    /**
     * 监听相关资源变化
     */
    gulp.task(`${id}-watch-copy`, () => {
      const copyList = this.copyList
      const copyFileList = copyList
        .map((copyFilePath) => {
          try {
            if (fs.statSync(path.join(srcPath, copyFilePath)).isDirectory()) {
              return path.join(copyFilePath, '**/*')
            } else {
              return copyFilePath
            }
          } catch (err) {
            // eslint-disable-next-line no-console
            console.error(err)
            return null
          }
        })
        .filter((copyFilePath) => !!copyFilePath)
      const watchCallback = (filePath) => copy([filePath])

      return gulp
        .watch(copyFileList, { cwd: srcPath, base: srcPath })
        .on('change', watchCallback)
        .on('add', watchCallback)
        .on('unlink', watchCallback)
    })

    /**
     * 监听 demo 变化
     */
    gulp.task(`${id}-watch-demo`, () => {
      const demoSrc = config.demoSrc
      const demoDist = config.demoDist
      const watchCallback = (filePath) =>
        gulp.src(filePath, { cwd: demoSrc, base: demoSrc }).pipe(gulp.dest(demoDist))
      return gulp
        .watch('**/*', { cwd: demoSrc, base: demoSrc })
        .on('change', watchCallback)
        .on('add', watchCallback)
        .on('unlink', watchCallback)
    })

    /**
     * 监听安装包列表变化
     */
    gulp.task(`${id}-watch-install`, () =>
      gulp.watch(path.resolve(__dirname, '../package.json'), install())
    )

    /**
     * 构建相关任务
     */
    gulp.task(
      `${id}-build`,
      gulp.series(
        `${id}-clean-dist`,
        `${id}-component-check`,
        gulp.parallel(
          `${id}-component-wxml`,
          `${id}-component-wxss`,
          `${id}-component-js`,
          `${id}-component-json`,
          `${id}-copy`
        )
      )
    )

    gulp.task(
      `${id}-watch`,
      gulp.series(
        `${id}-build`,
        `${id}-demo`,
        `${id}-install`,
        gulp.parallel(
          `${id}-watch-wxml`,
          `${id}-watch-wxss`,
          `${id}-watch-js`,
          `${id}-watch-json`,
          `${id}-watch-copy`,
          `${id}-watch-install`,
          `${id}-watch-demo`
        )
      )
    )

    gulp.task(`${id}-dev`, gulp.series(`${id}-build`, `${id}-demo`, `${id}-install`))

    gulp.task(`${id}-default`, gulp.series(`${id}-build`))
  }
}

module.exports = BuildTask
